import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import cv2
import numpy as np
from ultralytics import YOLO
import time
import threading
from typing import List, Tuple, Optional
import os
import json

class FenceEditor:
    """电子围栏编辑器：支持添加、删除、拖动、保存与加载围栏点"""
    def __init__(self, canvas_width=400, canvas_height=300):
        self.canvas_width = canvas_width
        self.canvas_height = canvas_height
        self.fence_points = []  # 围栏点列表
        self.dragging_point = None  # 正在拖动的点
        self.point_radius = 8  # 点的半径
        self.is_editing = False  # 是否处于编辑模式
        
        # 默认围栏（相对于画布的比例坐标）
        self.default_fence = [
            (0.25, 0.5), (0.75, 0.5), (0.8, 0.8), (0.2, 0.8)
        ]
        self.reset_fence()
    
    def reset_fence(self):
        """重置为默认围栏"""
        self.fence_points = [
            (int(x * self.canvas_width), int(y * self.canvas_height))
            for x, y in self.default_fence
        ]
    
    def add_point(self, x, y):
        """添加围栏点"""
        self.fence_points.append((x, y))
    
    def remove_point(self, index):
        """删除围栏点"""
        if 0 <= index < len(self.fence_points):
            del self.fence_points[index]
    
    def get_nearest_point(self, x, y):
        """获取距 (x, y) 最近的围栏点索引，若距离小于半径阈值则返回索引，否则返回 None"""
        min_dist = float('inf')
        nearest_index = -1
        
        for i, (px, py) in enumerate(self.fence_points):
            dist = ((x - px) ** 2 + (y - py) ** 2) ** 0.5
            if dist < min_dist and dist <= self.point_radius:
                min_dist = dist
                nearest_index = i
        
        return nearest_index if nearest_index >= 0 else None
    
    def update_point(self, index, x, y):
        """更新点位置"""
        if 0 <= index < len(self.fence_points):
            self.fence_points[index] = (x, y)
    
    def get_fence_polygon(self, scale_x=1.0, scale_y=1.0):
        """获取缩放后的围栏多边形"""
        if len(self.fence_points) < 3:
            return np.array([], dtype=np.int32)
        
        scaled_points = [
            (int(x * scale_x), int(y * scale_y))
            for x, y in self.fence_points
        ]
        return np.array(scaled_points, dtype=np.int32)
    
    def save_fence(self, filename):
        """保存围栏配置"""
        fence_data = {
            'fence_points': self.fence_points,
            'canvas_size': (self.canvas_width, self.canvas_height)
        }
        with open(filename, 'w') as f:
            json.dump(fence_data, f, indent=2)
    
    def load_fence(self, filename):
        """加载围栏配置"""
        try:
            with open(filename, 'r') as f:
                fence_data = json.load(f)
            self.fence_points = fence_data.get('fence_points', [])
            return True
        except Exception as e:
            print(f"加载围栏配置失败: {e}")
            return False

class PanoramicSurveillanceGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Panoramic Surveillance System")
        self.root.geometry("900x700")
        self.root.resizable(True, True)
        
        # 视频文件路径
        self.video_paths = {
            'left': tk.StringVar(),
            'center': tk.StringVar(),
            'right': tk.StringVar()
        }
        
        # 监控系统实例
        self.surveillance = None
        self.monitoring_thread = None
        self.is_monitoring = False
        
        # 围栏编辑器
        self.fence_editor = FenceEditor(400, 300)
        
        # 创建GUI界面
        self.create_widgets()
        
    def create_widgets(self):
        """创建GUI组件"""
        # 主标题
        title_label = tk.Label(self.root, text="Panoramic Surveillance System", 
                              font=("Arial", 16, "bold"))
        title_label.pack(pady=10)
        
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill="both", expand=True, padx=10, pady=5)
        
        # 左侧面板
        left_panel = ttk.Frame(main_frame)
        left_panel.pack(side="left", fill="both", expand=True, padx=(0, 5))
        
        # 右侧面板
        right_panel = ttk.Frame(main_frame)
        right_panel.pack(side="right", fill="y", padx=(5, 0))
        
        # 文件选择框架
        self.create_file_selection(left_panel)
        
        # 控制按钮框架
        self.create_control_buttons(left_panel)
        
        # 配置参数框架
        self.create_configuration(left_panel)
        
        # 状态显示框架
        self.create_status_display(left_panel)
        
        # 围栏编辑框架
        self.create_fence_editor(right_panel)
        
    def create_file_selection(self, parent):
        """创建文件选择区域"""
        file_frame = ttk.LabelFrame(parent, text="Video File Selection", padding=10)
        file_frame.pack(fill="x", pady=5)
        
        # 左视角视频选择
        self.create_file_selector(file_frame, "Left Camera:", self.video_paths['left'], 0)
        
        # 中央视角视频选择
        self.create_file_selector(file_frame, "Center Camera:", self.video_paths['center'], 1)
        
        # 右视角视频选择
        self.create_file_selector(file_frame, "Right Camera:", self.video_paths['right'], 2)
    
    def create_control_buttons(self, parent):
        """创建控制按钮区域"""
        control_frame = ttk.Frame(parent)
        control_frame.pack(fill="x", pady=10)
        
        # 开始监控按钮
        self.start_btn = ttk.Button(control_frame, text="Start Monitoring", 
                                   command=self.start_monitoring, style="Accent.TButton")
        self.start_btn.pack(side="left", padx=5)
        
        # 停止监控按钮
        self.stop_btn = ttk.Button(control_frame, text="Stop Monitoring", 
                                  command=self.stop_monitoring, state="disabled")
        self.stop_btn.pack(side="left", padx=5)
        
        # 夜视模式切换
        self.night_vision_var = tk.BooleanVar()
        self.night_vision_cb = ttk.Checkbutton(control_frame, text="Night Vision", 
                                              variable=self.night_vision_var,
                                              command=self.toggle_night_vision)
        self.night_vision_cb.pack(side="left", padx=20)
    
    def create_configuration(self, parent):
        """创建配置区域"""
        config_frame = ttk.LabelFrame(parent, text="Configuration", padding=10)
        config_frame.pack(fill="x", pady=5)
        
        # 置信度阈值
        ttk.Label(config_frame, text="Detection Confidence:").grid(row=0, column=0, sticky="w")
        self.confidence_var = tk.DoubleVar(value=0.5)
        confidence_scale = ttk.Scale(config_frame, from_=0.1, to=0.9, 
                                   variable=self.confidence_var, orient="horizontal")
        confidence_scale.grid(row=0, column=1, sticky="ew", padx=10)
        self.confidence_label = ttk.Label(config_frame, text="0.5")
        self.confidence_label.grid(row=0, column=2)
        
        # 绑定滑块事件
        confidence_scale.configure(command=self.update_confidence_label)
        
        config_frame.columnconfigure(1, weight=1)
    
    def create_status_display(self, parent):
        """创建状态显示区域"""
        status_frame = ttk.LabelFrame(parent, text="System Status", padding=10)
        status_frame.pack(fill="both", expand=True, pady=5)
        
        # 状态文本
        self.status_text = tk.Text(status_frame, height=6, wrap="word")
        scrollbar = ttk.Scrollbar(status_frame, orient="vertical", command=self.status_text.yview)
        self.status_text.configure(yscrollcommand=scrollbar.set)
        
        self.status_text.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        # 初始状态信息
        self.log_message("System initialized. Select video files and configure fence.")
    
    def create_fence_editor(self, parent):
        """创建围栏编辑区域"""
        fence_frame = ttk.LabelFrame(parent, text="Electronic Fence Editor", padding=10)
        fence_frame.pack(fill="both", expand=True)
        
        # 围栏编辑说明
        info_label = tk.Label(fence_frame, 
                             text="Click to add points\nDrag to move points\nRight-click to delete",
                             font=("Arial", 9), justify="left")
        info_label.pack(pady=5)
        
        # 围栏编辑画布
        self.fence_canvas = tk.Canvas(fence_frame, 
                                     width=self.fence_editor.canvas_width,
                                     height=self.fence_editor.canvas_height,
                                     bg="black", relief="sunken", bd=2)
        self.fence_canvas.pack(pady=5)
        
        # 绑定鼠标事件
        self.fence_canvas.bind("<Button-1>", self.on_canvas_click)
        self.fence_canvas.bind("<Button-3>", self.on_canvas_right_click)
        self.fence_canvas.bind("<B1-Motion>", self.on_canvas_drag)
        self.fence_canvas.bind("<ButtonRelease-1>", self.on_canvas_release)
        
        # 围栏控制按钮
        fence_control_frame = ttk.Frame(fence_frame)
        fence_control_frame.pack(fill="x", pady=5)
        
        ttk.Button(fence_control_frame, text="Reset", 
                  command=self.reset_fence).pack(side="left", padx=2)
        ttk.Button(fence_control_frame, text="Save", 
                  command=self.save_fence).pack(side="left", padx=2)
        ttk.Button(fence_control_frame, text="Load", 
                  command=self.load_fence).pack(side="left", padx=2)
        
        # 编辑模式切换
        self.edit_mode_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(fence_control_frame, text="Edit Mode", 
                       variable=self.edit_mode_var,
                       command=self.toggle_edit_mode).pack(side="right")
        
        # 初始绘制围栏
        self.draw_fence()
    
    def create_file_selector(self, parent, label_text, string_var, row):
        """创建文件选择器"""
        ttk.Label(parent, text=label_text).grid(row=row, column=0, sticky="w", pady=5)
        
        entry = ttk.Entry(parent, textvariable=string_var, width=40)
        entry.grid(row=row, column=1, sticky="ew", padx=10, pady=5)
        
        browse_btn = ttk.Button(parent, text="Browse", 
                               command=lambda: self.browse_file(string_var))
        browse_btn.grid(row=row, column=2, pady=5)
        
        parent.columnconfigure(1, weight=1)
    
    def on_canvas_click(self, event):
        """画布点击事件"""
        if not self.edit_mode_var.get():
            return
        
        x, y = event.x, event.y
        
        # 检查是否点击了现有点
        point_index = self.fence_editor.get_nearest_point(x, y)
        
        if point_index is not None:
            # 开始拖动现有点
            self.fence_editor.dragging_point = point_index
        else:
            # 添加新点
            self.fence_editor.add_point(x, y)
            self.draw_fence()
            self.log_message(f"Added fence point at ({x}, {y})")
    
    def on_canvas_right_click(self, event):
        """画布右键点击事件"""
        if not self.edit_mode_var.get():
            return
        
        x, y = event.x, event.y
        point_index = self.fence_editor.get_nearest_point(x, y)
        
        if point_index is not None and len(self.fence_editor.fence_points) > 3:
            self.fence_editor.remove_point(point_index)
            self.draw_fence()
            self.log_message(f"Removed fence point {point_index}")
    
    def on_canvas_drag(self, event):
        """画布拖动事件"""
        if not self.edit_mode_var.get() or self.fence_editor.dragging_point is None:
            return
        
        x = max(0, min(event.x, self.fence_editor.canvas_width))
        y = max(0, min(event.y, self.fence_editor.canvas_height))
        
        self.fence_editor.update_point(self.fence_editor.dragging_point, x, y)
        self.draw_fence()
    
    def on_canvas_release(self, event):
        """画布释放事件"""
        if self.fence_editor.dragging_point is not None:
            self.log_message(f"Moved fence point {self.fence_editor.dragging_point}")
            self.fence_editor.dragging_point = None
    
    def draw_fence(self):
        """绘制围栏"""
        self.fence_canvas.delete("all")
        
        if len(self.fence_editor.fence_points) < 2:
            return
        
        # 绘制围栏线条
        points = self.fence_editor.fence_points
        for i in range(len(points)):
            x1, y1 = points[i]
            x2, y2 = points[(i + 1) % len(points)]
            self.fence_canvas.create_line(x1, y1, x2, y2, fill="yellow", width=2)
        
        # 绘制围栏点
        for i, (x, y) in enumerate(points):
            r = self.fence_editor.point_radius
            color = "red" if self.edit_mode_var.get() else "orange"
            self.fence_canvas.create_oval(x-r, y-r, x+r, y+r, 
                                         fill=color, outline="white", width=2)
            
            # 显示点编号
            if self.edit_mode_var.get():
                self.fence_canvas.create_text(x, y, text=str(i), fill="white", font=("Arial", 8, "bold"))
    
    def toggle_edit_mode(self):
        """切换编辑模式"""
        self.draw_fence()
        mode = "enabled" if self.edit_mode_var.get() else "disabled"
        self.log_message(f"Fence edit mode {mode}")
    
    def reset_fence(self):
        """重置围栏"""
        self.fence_editor.reset_fence()
        self.draw_fence()
        self.log_message("Fence reset to default")
    
    def save_fence(self):
        """保存围栏配置"""
        filename = filedialog.asksaveasfilename(
            title="Save Fence Configuration",
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        if filename:
            self.fence_editor.save_fence(filename)
            self.log_message(f"Fence saved to {os.path.basename(filename)}")
    
    def load_fence(self):
        """加载围栏配置"""
        filename = filedialog.askopenfilename(
            title="Load Fence Configuration",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        if filename and self.fence_editor.load_fence(filename):
            self.draw_fence()
            self.log_message(f"Fence loaded from {os.path.basename(filename)}")
    
    def browse_file(self, string_var):
        """浏览文件对话框"""
        filename = filedialog.askopenfilename(
            title="Select Video File",
            filetypes=[("Video files", "*.mp4 *.avi *.mov *.mkv"), ("All files", "*.*")]
        )
        if filename:
            string_var.set(filename)
            self.log_message(f"Selected: {os.path.basename(filename)}")
    
    def update_confidence_label(self, value):
        """更新置信度标签"""
        self.confidence_label.config(text=f"{float(value):.1f}")
        if self.surveillance:
            self.surveillance.confidence_threshold = float(value)
    
    def toggle_night_vision(self):
        """切换夜视模式"""
        if self.surveillance:
            self.surveillance.night_vision_enabled = self.night_vision_var.get()
            status = "enabled" if self.night_vision_var.get() else "disabled"
            self.log_message(f"Night vision mode {status}")
    
    def validate_files(self):
        """验证文件选择"""
        for camera, path_var in self.video_paths.items():
            path = path_var.get().strip()
            if not path:
                messagebox.showerror("Error", f"Please select {camera} camera video file")
                return False
            if not os.path.exists(path):
                messagebox.showerror("Error", f"{camera} camera video file not found: {path}")
                return False
        return True
    
    def start_monitoring(self):
        """开始监控"""
        if not self.validate_files():
            return
        
        if len(self.fence_editor.fence_points) < 3:
            messagebox.showerror("Error", "Please define at least 3 fence points")
            return
        
        try:
            # 创建监控系统实例
            self.surveillance = PanoramicSurveillanceCore(
                left_path=self.video_paths['left'].get(),
                center_path=self.video_paths['center'].get(),
                right_path=self.video_paths['right'].get(),
                confidence_threshold=self.confidence_var.get(),
                fence_editor=self.fence_editor,
                gui_callback=self.log_message
            )
            
            # 设置夜视模式
            self.surveillance.night_vision_enabled = self.night_vision_var.get()
            
            # 启动监控线程
            self.is_monitoring = True
            self.monitoring_thread = threading.Thread(target=self.run_monitoring, daemon=True)
            self.monitoring_thread.start()
            
            # 更新按钮状态
            self.start_btn.config(state="disabled")
            self.stop_btn.config(state="normal")
            
            self.log_message("Monitoring started with custom fence!")
            
        except Exception as e:
            messagebox.showerror("Error", f"Failed to start monitoring: {str(e)}")
            self.log_message(f"Error: {str(e)}")
    
    def stop_monitoring(self):
        """停止监控"""
        self.is_monitoring = False
        
        if self.surveillance:
            self.surveillance.stop()
        
        # 更新按钮状态
        self.start_btn.config(state="normal")
        self.stop_btn.config(state="disabled")
        
        self.log_message("Monitoring stopped.")
    
    def run_monitoring(self):
        """运行监控循环"""
        try:
            self.surveillance.run()
        except Exception as e:
            self.log_message(f"Monitoring error: {str(e)}")
        finally:
            # 重置按钮状态
            self.root.after(0, lambda: (
                self.start_btn.config(state="normal"),
                self.stop_btn.config(state="disabled")
            ))
    
    def log_message(self, message):
        """记录日志消息"""
        timestamp = time.strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"
        
        # 在主线程中更新GUI
        self.root.after(0, lambda: (
            self.status_text.insert(tk.END, log_entry),
            self.status_text.see(tk.END)
        ))
    
    def run(self):
        """运行GUI"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.root.mainloop()
    
    def on_closing(self):
        """关闭窗口时的处理"""
        if self.is_monitoring:
            self.stop_monitoring()
        self.root.destroy()

class PanoramicSurveillanceCore:
    """监控系统核心类 - 增强版"""
    def __init__(self, left_path, center_path, right_path, confidence_threshold=0.5, 
                 fence_editor=None, gui_callback=None):
        # 初始化YOLO模型
        self.yolo_model = YOLO('yolov8n.pt')
        
        # 视频路径
        self.video_paths = {
            'left': left_path,
            'center': center_path,
            'right': right_path
        }
        
        # 视频捕获对象
        self.caps = {}
        
        # 配置参数
        self.confidence_threshold = confidence_threshold
        self.night_vision_enabled = False
        self.gui_callback = gui_callback
        self.running = False
        
        # 围栏编辑器
        self.fence_editor = fence_editor
        
        # 俯视图变换矩阵
        self.bird_view_matrix = None
        self.setup_bird_view_transform()
        
        # 拼接参数
        self.stitch_width = 1200
        self.stitch_height = 400
    
    def setup_bird_view_transform(self):
        """设置俯视图变换矩阵"""
        src_points = np.float32([
            [100, 150], [1100, 150], [1100, 350], [100, 350]
        ])
        dst_points = np.float32([
            [50, 50], [350, 50], [350, 350], [50, 350]
        ])
        self.bird_view_matrix = cv2.getPerspectiveTransform(src_points, dst_points)
    
    def log(self, message):
        """日志记录"""
        if self.gui_callback:
            self.gui_callback(message)
        else:
            print(message)
    
    def get_fence_polygon(self):
        """获取当前围栏多边形"""
        if self.fence_editor:
            # 计算从编辑器画布到实际视频的缩放比例
            scale_x = self.stitch_width / self.fence_editor.canvas_width
            scale_y = self.stitch_height / self.fence_editor.canvas_height
            return self.fence_editor.get_fence_polygon(scale_x, scale_y)
        else:
            # 默认围栏
            return np.array([
                [300, 200], [900, 200], [1000, 350], [200, 350]
            ], np.int32)
    
    def initialize_videos(self):
        """初始化视频捕获"""
        for name, path in self.video_paths.items():
            cap = cv2.VideoCapture(path)
            if not cap.isOpened():
                raise Exception(f"Cannot open video file: {path}")
            self.caps[name] = cap
            self.log(f"Initialized {name} camera: {os.path.basename(path)}")
        return True
    
    def read_frames(self):
        """同步读取三路视频帧"""
        frames = {}
        for name, cap in self.caps.items():
            ret, frame = cap.read()
            if not ret:
                # 视频结束，重新开始
                cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                ret, frame = cap.read()
                if not ret:
                    return None
            frames[name] = frame
        return frames['left'], frames['center'], frames['right']
    
    def stitch_frames(self, left_frame, center_frame, right_frame):
        """拼接三路视频帧"""
        target_height = self.stitch_height
        target_width = self.stitch_width // 3
        
        left_resized = cv2.resize(left_frame, (target_width, target_height))
        center_resized = cv2.resize(center_frame, (target_width, target_height))
        right_resized = cv2.resize(right_frame, (target_width, target_height))
        
        panoramic = np.hstack([left_resized, center_resized, right_resized])
        return panoramic
    
    def enhance_night_vision(self, frame):
        """夜视增强：使用 CLAHE 算法增强 LAB 颜色空间亮度通道，提升图像清晰度"""
        if not self.night_vision_enabled:
            return frame
        
        lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
        l, a, b = cv2.split(lab)
        
        clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
        l = clahe.apply(l)
        
        enhanced_lab = cv2.merge([l, a, b])
        enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
        return enhanced
    
    def detect_humans(self, frame):
        """使用 YOLOv8 检测图像中的人体目标，返回置信度高于阈值的检测框列表"""
        results = self.yolo_model(frame, classes=[0])
        
        detections = []
        if results[0].boxes is not None:
            for box in results[0].boxes:
                x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
                confidence = box.conf[0].cpu().numpy()
                
                if confidence > self.confidence_threshold:
                    detections.append((int(x1), int(y1), int(x2), int(y2), float(confidence)))
        
        return detections
    
    def is_in_fence(self, x, y):
        """判断一个点 (x, y) 是否位于定义的电子围栏多边形内"""
        fence_polygon = self.get_fence_polygon()
        if len(fence_polygon) < 3:
            return False
        
        result = cv2.pointPolygonTest(fence_polygon, (x, y), False)
        return result >= 0
    
    def get_foot_position(self, x1, y1, x2, y2):
        """根据检测框左上角和右下角坐标，计算出人体脚部中心点位置"""
        return (x1 + x2) // 2, y2
    
    def transform_to_bird_view(self, x, y):
        """转换到俯视图坐标"""
        point = np.array([[[x, y]]], dtype=np.float32)
        transformed = cv2.perspectiveTransform(point, self.bird_view_matrix)
        return int(transformed[0][0][0]), int(transformed[0][0][1])
    
    def draw_detections(self, frame, detections):
        """绘制检测结果"""
        result_frame = frame.copy()
        
        # 绘制电子围栏
        fence_polygon = self.get_fence_polygon()
        if len(fence_polygon) >= 3:
            cv2.polylines(result_frame, [fence_polygon], True, (0, 255, 255), 2)
            cv2.putText(result_frame, "Custom Electronic Fence", 
                       (fence_polygon[0][0], fence_polygon[0][1] - 10),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
        
        alert_count = 0
        for x1, y1, x2, y2, conf in detections:
            foot_x, foot_y = self.get_foot_position(x1, y1, x2, y2)
            in_fence = self.is_in_fence(foot_x, foot_y)
            
            if in_fence:
                alert_count += 1
            
            color = (0, 0, 255) if in_fence else (0, 255, 0)
            
            cv2.rectangle(result_frame, (x1, y1), (x2, y2), color, 2)
            
            label = f'Person {conf:.2f}'
            if in_fence:
                label += ' [ALERT]'
            
            cv2.putText(result_frame, label, (x1, y1 - 10),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
            cv2.circle(result_frame, (foot_x, foot_y), 5, color, -1)
        
        return result_frame, alert_count
    
    def create_bird_view(self, frame, detections):
        """创建俯视图"""
        bird_view = cv2.warpPerspective(frame, self.bird_view_matrix, (400, 400))
        
        for x1, y1, x2, y2, conf in detections:
            foot_x, foot_y = self.get_foot_position(x1, y1, x2, y2)
            bird_x, bird_y = self.transform_to_bird_view(foot_x, foot_y)
            
            in_fence = self.is_in_fence(foot_x, foot_y)
            color = (0, 0, 255) if in_fence else (0, 255, 0)
            
            if 0 <= bird_x < 400 and 0 <= bird_y < 400:
                cv2.circle(bird_view, (bird_x, bird_y), 8, color, -1)
                cv2.putText(bird_view, f'{conf:.1f}', (bird_x + 10, bird_y),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
        
        return bird_view
    
    def combine_views(self, main_view, bird_view):
        """组合视图"""
        bird_resized = cv2.resize(bird_view, (400, 300))
        
        combined_height = max(main_view.shape[0], bird_resized.shape[0])
        combined_width = main_view.shape[1] + bird_resized.shape[1]
        
        combined = np.zeros((combined_height, combined_width, 3), dtype=np.uint8)
        combined[:main_view.shape[0], :main_view.shape[1]] = main_view
        combined[:bird_resized.shape[0], main_view.shape[1]:] = bird_resized
        
        return combined
    
    def run(self):
        """主运行循环"""
        if not self.initialize_videos():
            return
        
        self.running = True
        self.log("Monitoring started with custom fence")
        
        frame_count = 0
        try:
            while self.running:
                frames = self.read_frames()
                if frames is None:
                    break
                
                left_frame, center_frame, right_frame = frames
                
                # 拼接和处理
                panoramic = self.stitch_frames(left_frame, center_frame, right_frame)
                enhanced = self.enhance_night_vision(panoramic)
                detections = self.detect_humans(enhanced)
                
                # 绘制和显示
                main_view, alert_count = self.draw_detections(enhanced, detections)
                bird_view = self.create_bird_view(enhanced, detections)
                final_view = self.combine_views(main_view, bird_view)
                
                # 添加状态信息
                status_text = f"Night Vision: {'ON' if self.night_vision_enabled else 'OFF'} | Detected: {len(detections)} | Alerts: {alert_count}"
                cv2.putText(final_view, status_text, (10, 30),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
                
                # 添加按键提示信息
                key_info_text = "Press 'Q' to quit | Press 'N' to toggle night vision"
                cv2.putText(final_view, key_info_text, (10, 60),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
                
                cv2.imshow('Panoramic Surveillance System', final_view)
                
                # 处理按键（不区分大小写）
                key = cv2.waitKey(1) & 0xFF
                if key == ord('q') or key == ord('Q'):
                    break
                elif key == ord('n') or key == ord('N'):
                    self.night_vision_enabled = not self.night_vision_enabled
                    self.log(f"Night vision: {'ON' if self.night_vision_enabled else 'OFF'}")
                
                # 定期报告状态
                frame_count += 1
                if frame_count % 300 == 0:  # 每10秒报告一次
                    self.log(f"Processing frame {frame_count}, detected {len(detections)} persons, {alert_count} alerts")
                
                time.sleep(0.03)
                
        except Exception as e:
            self.log(f"Error during monitoring: {str(e)}")
        finally:
            self.stop()
    
    def stop(self):
        """停止监控"""
        self.running = False
        for cap in self.caps.values():
            cap.release()
        cv2.destroyAllWindows()
        self.log("Monitoring stopped and resources cleaned")

if __name__ == "__main__":
    app = PanoramicSurveillanceGUI()
    app.run()